﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries.Utilities;

namespace NetTopologySuite.Simplify
{
	public class TopologyPreservingSimplifier
	{
		public static IGeometry Simplify(IGeometry geom, double distanceTolerance)
		{
			return new TopologyPreservingSimplifier(geom)
			{
				DistanceTolerance = distanceTolerance
			}.GetResultGeometry();
		}

		public TopologyPreservingSimplifier(IGeometry inputGeom)
		{
			this._inputGeom = inputGeom;
		}

		public double DistanceTolerance
		{
			get
			{
				return this._lineSimplifier.DistanceTolerance;
			}
			set
			{
				this._lineSimplifier.DistanceTolerance = value;
			}
		}

		public IGeometry GetResultGeometry()
		{
			if (this._inputGeom.IsEmpty)
			{
				return (IGeometry)this._inputGeom.Clone();
			}
			this._lineStringMap = new Dictionary<ILineString, TaggedLineString>();
			TopologyPreservingSimplifier.LineStringMapBuilderFilter filter = new TopologyPreservingSimplifier.LineStringMapBuilderFilter(this);
			this._inputGeom.Apply(filter);
			this._lineSimplifier.Simplify(this._lineStringMap.Values);
			return new TopologyPreservingSimplifier.LineStringTransformer(this).Transform(this._inputGeom);
		}

		private readonly IGeometry _inputGeom;

		private readonly TaggedLinesSimplifier _lineSimplifier = new TaggedLinesSimplifier();

		private Dictionary<ILineString, TaggedLineString> _lineStringMap;

		private class LineStringTransformer : GeometryTransformer
		{
			public LineStringTransformer(TopologyPreservingSimplifier container)
			{
				this._container = container;
			}

			protected override ICoordinateSequence TransformCoordinates(ICoordinateSequence coords, IGeometry parent)
			{
				if (coords.Count == 0)
				{
					return null;
				}
				ILineString lineString = parent as ILineString;
				if (lineString != null)
				{
					TaggedLineString taggedLineString = this._container._lineStringMap[lineString];
					return this.CreateCoordinateSequence(taggedLineString.ResultCoordinates);
				}
				return base.TransformCoordinates(coords, parent);
			}

			private readonly TopologyPreservingSimplifier _container;
		}

		private class LineStringMapBuilderFilter : IGeometryComponentFilter
		{
			public LineStringMapBuilderFilter(TopologyPreservingSimplifier container)
			{
				this._container = container;
			}

			public void Filter(IGeometry geom)
			{
				ILineString lineString = geom as ILineString;
				if (lineString == null)
				{
					return;
				}
				if (lineString.IsEmpty)
				{
					return;
				}
				int minimumSize = lineString.IsClosed ? 4 : 2;
				TaggedLineString value = new TaggedLineString(lineString, minimumSize);
				this._container._lineStringMap.Add(lineString, value);
			}

			private readonly TopologyPreservingSimplifier _container;
		}
	}
}
